package utils

import (
	"errors"
	"github.com/labstack/echo/v4"
	"rabbit_love/src/utils/response"
	"strings"
	"time"

	"github.com/dgrijalva/jwt-go"
)

/*
	规范: 基础组件,不应调用本脚步之外的任何自定义部函数。
	应用:
		1. go get github.com/dgrijalva/jwt-go
		2. login 时返回 GetToken, 下次请求时前端需带上 token。
		3. 路由中添加 handler
			统计添加 handler:
				admin := r.Group("/api/admin")
				admin.Use(utils.CheckToken)
			或 针对路由单独缇添加
				login.GET("/hello", CheckToken, helloHandler)
	其它:
		JwtClaims 可根据需要添加或修改。
		JwtSecret 需重新设置(随意设置,中英文都行)
	[参考](https://www.cnblogs.com/marshhu/p/12639633.html)
*/

// JwtClaims 自定义 claims (TODO, 根据需要修改)
type JwtClaims struct {
	ID       int    `json:"id"`
	LoverId  int    `json:"loverId"`
	UserName string `json:"userName"`
	jwt.StandardClaims
}

// JwtSecret 密钥
var JwtSecret = []byte("RabbitLove") // (TODO, 根据需要修改)

// TokenExpireDuration token 有效时	间
const TokenExpireDuration = time.Hour * 24 * 365

// GetToken 生成 token (TODO, 根据需要修改)
func GetToken(id, loverId int, userName string) (string, error) {
	c := JwtClaims{
		id,
		loverId,
		userName,
		jwt.StandardClaims{
			ExpiresAt: time.Now().Add(TokenExpireDuration).Unix(), // 过期时间
			Issuer:    "my-project",                               // 签发人
		},
	}
	// 使用指定的签名方法创建签名对象
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, c)
	// 使用指定的 secret 签名并获得完整的编码后的字符串 token
	return token.SignedString(JwtSecret)
}

// CheckToken 校验 token
func CheckToken(handle echo.HandlerFunc) echo.HandlerFunc {
	return func(ctx echo.Context) error {
		authHeader := ctx.Request().Header.Get("Authorization")
		if authHeader == "" {
			return response.RespErr(ctx, "请求头中auth为空")
		}
		// 按空格分割
		parts := strings.SplitN(authHeader, " ", 2)
		if !(len(parts) == 2 && parts[0] == "Bearer") {
			return response.RespErr(ctx, "无法解析")
		}
		// parts[1]是获取到的 tokenString, 我们使用之前定义好的解析 JWT 的函数来解析它
		tokenC, err := ParseToken(parts[1])
		if err != nil {
			return response.RespErr(ctx, "Token错误")
		}

		// 将当前请求的信息保存到请求的上下文 c 上 (TODO, 根据需要修改)
		ctx.Set("userID", tokenC.ID)
		ctx.Set("userName", tokenC.UserName)
		if tokenC.LoverId != 0 {
			ctx.Set("loverId", tokenC.LoverId)
		}
		// 后续的处理函数可以用过 c.Get("ID") 来获取当前请求的用户信息
		return handle(ctx)
	}
}

// ParseToken 解析 Jwt 并校验
func ParseToken(tokenString string) (*JwtClaims, error) {
	// 解析 token
	token, err := jwt.ParseWithClaims(tokenString, &JwtClaims{}, func(token *jwt.Token) (i interface{}, err error) {
		return JwtSecret, nil
	})
	if err != nil {
		return nil, err
	}
	// 校验 token
	if claims, ok := token.Claims.(*JwtClaims); ok && token.Valid {
		return claims, nil
	}
	return nil, errors.New("invalid token")
}
